#include "ustc_comdef.h"
#include "ustc_custom.h"
#include "ustc_api.h"
#include "ustc_usb_scsi.h"
#include "ustc_usb_bulk.h"
#include "ustc_usb_drv.h"

extern USB_PATH_T *gp_usb_path;
PARA_USB_GLOABL g_ustc_usb_contorl;

/*global structure of usb module*/
void bulk_cbw_handler(void)
{
	if (bulk_is_cbw_valid(gp_usb_path->ep_buff_rx,gp_usb_path->ep_rx_len))
	{
		ustc_memcpy(&(gp_usb_path->bulk_hdr),gp_usb_path->ep_buff_rx,sizeof(CBW));
		scsi_handler();
		return;
	}
	// for Invalid CBW,length wrong?
	// Stall Both Bulk Endpoints, Let host goto reset recovery sequence
	bulk_set_csw_status(CASECBW,gp_usb_path->bulk_hdr.cbw.dCBW_DataXferLen);
	//RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0);
	gp_usb_path->bot_state = BOTFSM_CSW;
}

//set CSW return info
//HostDevCase: situation type
//len: have processed length;(set as the length we havn't transfered)
void bulk_set_csw_status(USTC_U8 HostDevCase,USTC_U16 len)
{
	gp_usb_path->bulk_hdr.csw.dCSW_Signature = CSW_SIGNATURE;
	gp_usb_path->bulk_hdr.csw.dCSW_DataResidue = len;

	switch(HostDevCase)
	{
	case CASEOK:
	case CASE1:     //Hn=Dn
	case CASE6:     // Hi=Di
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_GOOD;
		break;
	case CASE12:    //Ho=Do
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_GOOD;
		break;
	case CASE2:     //Hn<Di
	case CASE3:     //Hn<Do
		//BOTBF_StallAtBulkIn = 1; // may or may-not
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_PHASE_ERROR;
		break;
	case CASE4:     // Hi>Dn
	case CASE5:    //Hi>Di
		//stall bulk in ep
		usb_stall_ep(3,1);
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_FAIL;//CSW_GOOD or CSW_FAIL
		break;
	case CASE7:     //Hi<Di
	case CASE8:     //Hi<>Do
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_PHASE_ERROR;
		break;
	case CASE9:     //Ho>Dn
	case CASE11:    //Ho>Do
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_FAIL;//CSW_GOOD or CSW_FAIL
		break;
	case CASE10:    // Ho<>Di 
	case CASE13:    //Ho<Do
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_PHASE_ERROR;
		break;
	case CASECBW:   /* invalid CBW */
		usb_stall_ep(2,1);
		usb_stall_ep(3,1);
        	gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_PHASE_ERROR;
		break;
	case CASECMDFAIL:
		usb_stall_ep(2,1);
	       usb_stall_ep(3,1);
		gp_usb_path->bulk_hdr.csw.bCSW_Status = CSW_FAIL;
		break;
	default:
		break;
	}
}

void bulk_csw_handler(void)
{
	ustc_memcpy(gp_usb_path->ep_buff_tx,&(gp_usb_path->bulk_hdr),sizeof(CSW));
	usb_write_ep(1,gp_usb_path->ep_buff_tx,sizeof(CSW));
	gp_usb_path->bot_state = BOTFSM_IDLE;	
}

//check if a received PACKET is a valid CBW packet
//buf: received buffer
//return: 1 valid CBW  0 invalid
int bulk_is_cbw_valid(unsigned char* buf,unsigned char len)
{
	PBULK_HEADER_T	cbwhdr = (PBULK_HEADER_T)buf;
	//return 0;
	if (len < 15)
	{
		return 0;
	}	
	if (len < cbwhdr->cbw.bCBW_CDBLen +15)
	{
		return 0;
	}	
	if( cbwhdr->cbw.dCBW_Signature == CBW_SIGNATURE && cbwhdr->cbw.bCBW_CDBLen <= MAX_CDBLEN )
		return (1);
	else
	{
		return 0;
	}
}

/*FOR USB BOOT*/
STATUS_T usbboot_read_multi_block(void * p_buffer, unsigned int block_index,unsigned int block_num) 
{
	if (block_index ==0)
	{
		ustc_memset(p_buffer,0xffffffff,block_num*512);
		*((USTC_U8*)p_buffer+510) = 0x55;
		*((USTC_U8*)p_buffer+511) = 0xaa;
	}
	else
		ustc_memset(p_buffer,0xffffffff,block_num*512);
	return RSP_SUCCESS;
}
STATUS_T usbboot_write_multi_block(void * p_buffer, unsigned int block_index,unsigned int block_num) 
{
		return RSP_SUCCESS;
}

void bulk_data_in_handler(void)
{
	unsigned char	len=0;
	STATUS_T status = RSP_SUCCESS;
	USTC_U8	retry_count;
	if (gp_usb_path->usb_sec_num >0)
	{
		//using mutiblock read
		//first read the sector to read to our cache buffer
		if (gp_usb_path->usb_sec_off ==0)
		{
			if(0==g_ustc_usb_contorl.sys_startmode)
			{
				if (gp_usb_path->usb_sec_num > MAX_CACHE_BLOCK)
					gp_usb_path->sec_num = MAX_CACHE_BLOCK;
				else
					gp_usb_path->sec_num = gp_usb_path->usb_sec_num;

				#if 0
				if (test >=3)
				{
					test++;
					if (test >3)
						test = 10;
				}
				#endif		

				retry_count = gp_usb_path->p_para_usb->udisk_retry_count;
				do
				{
					status = gp_usb_path->p_para_usb->read[gp_usb_path->udisk_disk_src]
						((USTC_U32*)gp_usb_path->usb_cache,
						gp_usb_path->p_para_usb->udisk_disk_start[gp_usb_path->udisk_disk_index]+gp_usb_path->usb_start_lba,
						gp_usb_path->sec_num);
				}while ((status != RSP_SUCCESS)&&(retry_count-- >0));
				if (status != RSP_SUCCESS)//retry 5 time,still error; stall ep
				{
					bulk_set_csw_status(CASECMDFAIL,gp_usb_path->bulk_hdr.cbw.dCBW_DataXferLen);
					gp_usb_path->bot_state = BOTFSM_CSW;
				}

				#if 0			
				*test_ptr++ = 0x5555;
				*test_ptr++ = gp_usb_path->usb_start_lba;
				*test_ptr++ = gp_usb_path->sec_num;
				if (test >=3)
					test = 10;
				
				#endif		
				
				//process lock disk; return all '1' data except boot sector
				if (gp_usb_path->p_para_usb->udisk_disk_flag[4*gp_usb_path->udisk_disk_src+gp_usb_path->udisk_disk_index] & 2)
				{
					if (gp_usb_path->usb_start_lba ==0)
						ustc_memset32(gp_usb_path->usb_cache+512,0xffffffff,(gp_usb_path->sec_num-1)*512);
					else
						ustc_memset32(gp_usb_path->usb_cache,0xffffffff,gp_usb_path->sec_num*512);
				}
			}	
			else
			{
#ifdef AP_USB_FIXBUG
				//add by guosheng
				if (gp_usb_path->usb_sec_num >MAX_CACHE_BLOCK)
					gp_usb_path->sec_num =MAX_CACHE_BLOCK;
				else
					gp_usb_path->sec_num = gp_usb_path->usb_sec_num;
#endif
				usbboot_read_multi_block(gp_usb_path->usb_cache, gp_usb_path->usb_start_lba, gp_usb_path->sec_num);
			}
			gp_usb_path->bot_data = (USTC_U8*)gp_usb_path->usb_cache;
		}	
		len =EP1_PACKET_SIZE;
		if (gp_usb_path->usb_sec_off + len >= 512*(gp_usb_path->sec_num))
		{
			//len = 512*gp_usb_path->sec_num - gp_usb_path->usb_sec_off;
			gp_usb_path->usb_start_lba += gp_usb_path->sec_num;
			gp_usb_path->usb_sec_num -= gp_usb_path->sec_num;
			gp_usb_path->usb_sec_off = 0;
		}
		else
			gp_usb_path->usb_sec_off += len;
		usb_write_ep(1,gp_usb_path->bot_data,len);
		gp_usb_path->bot_data += len;
		gp_usb_path->bot_len -=len;
		gp_usb_path->bot_len_transfer +=len;

		if ((gp_usb_path->bot_len ==0) || (gp_usb_path->usb_sec_num ==0))
		{
			bulk_set_csw_status(CASEOK,gp_usb_path->bulk_hdr.csw.dCSW_DataResidue - gp_usb_path->bot_len_transfer);
				//RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0);
			gp_usb_path->bot_state = BOTFSM_CSW;
		}
	}
	else
	{
		if (gp_usb_path->bot_len > EP1_PACKET_SIZE)
			len = EP1_PACKET_SIZE;
		else
			len = gp_usb_path->bot_len;
		usb_write_ep(1,gp_usb_path->bot_data,len);
		gp_usb_path->bot_data += len;
		gp_usb_path->bot_len -=len;
		gp_usb_path->bot_len_transfer +=len;
		
		if (gp_usb_path->bot_len ==0)
		{
				bulk_set_csw_status(CASEOK,gp_usb_path->bulk_hdr.csw.dCSW_DataResidue - gp_usb_path->bot_len_transfer);
				//RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0);
				gp_usb_path->bot_state = BOTFSM_CSW;
		}
	}	
}

void bulk_data_out_handler(void)
{
	//unsigned char	len=0;
	unsigned char	wlen =0;
		
	wlen = gp_usb_path->ep_rx_len;
	if ((wlen >0))
	{
		if (gp_usb_path->usb_sec_num >0)
		{
			//multi block
			//len =EP1_PACKET_SIZE;
			if (gp_usb_path->usb_sec_off + wlen >= 512 *gp_usb_path->sec_num)
			{
				//len = 512 - gp_usb_path->usb_sec_off;
				ustc_memcpy_32(gp_usb_path->usb_cache+gp_usb_path->usb_sec_off,gp_usb_path->ep_buff_rx,wlen>>2);

				if (0 == g_ustc_usb_contorl.sys_startmode)
				{
					gp_usb_path->p_para_usb->write[gp_usb_path->udisk_disk_src]
						((USTC_U32*)gp_usb_path->usb_cache,
						gp_usb_path->p_para_usb->udisk_disk_start[gp_usb_path->udisk_disk_index]+gp_usb_path->usb_start_lba,
						gp_usb_path->sec_num);
				}
				else
				{
					usbboot_write_multi_block(gp_usb_path->usb_cache, gp_usb_path->usb_start_lba, gp_usb_path->sec_num);
				}
				#if 0		
					*test_ptr++ = 0xaaaa;
					*test_ptr++ = gp_usb_path->usb_start_lba;
					*test_ptr++ = gp_usb_path->sec_num;
					if (gp_usb_path->usb_start_lba == 0x13f)
						test++;
					if (test >0)
						test ++;
				#endif		
				gp_usb_path->usb_start_lba += gp_usb_path->sec_num;
				gp_usb_path->usb_sec_num -= gp_usb_path->sec_num;
				gp_usb_path->usb_sec_off = 0;

				if (gp_usb_path->usb_sec_num >MAX_CACHE_BLOCK)
					gp_usb_path->sec_num =MAX_CACHE_BLOCK;
				else
					gp_usb_path->sec_num = gp_usb_path->usb_sec_num;
			}
			else
			{
				ustc_memcpy_32(gp_usb_path->usb_cache+gp_usb_path->usb_sec_off,gp_usb_path->ep_buff_rx,wlen>>2);
				gp_usb_path->usb_sec_off += wlen;
			}

			gp_usb_path->bot_len -=wlen;
			gp_usb_path->bot_len_transfer +=wlen;
			
			if ((gp_usb_path->bot_len ==0) || (gp_usb_path->usb_sec_num ==0))
			{
				bulk_set_csw_status(CASEOK,gp_usb_path->bulk_hdr.csw.dCSW_DataResidue - gp_usb_path->bot_len_transfer);
				//RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0);
				gp_usb_path->bot_state = BOTFSM_CSW;
				return;
			}
		}
		else//process vendor_set_info
		{
#ifdef AP_USB_FIXBUG			

				#if 0
				if (gp_usb_path->usb_sec_off + wlen >= 512)
				{
					//len = 512 - gp_usb_path->usb_sec_off;
					ustc_memcpy_32(gp_usb_path->usb_cache+gp_usb_path->usb_sec_off,gp_usb_path->ep_buff_rx,wlen>>2);
					//store the info
					usb_store_isector(gp_usb_path->p_para_usb,gp_usb_path->usb_cache,gp_usb_path->udisk_disk_src));	
					gp_usb_path->usb_sec_off = 0;
					
				}
				else
				#endif
			ustc_memcpy_32((USTC_U32 *)gp_usb_path->bot_data, gp_usb_path->ep_buff_rx, wlen>>2);
			gp_usb_path->bot_data+=wlen;
			gp_usb_path->bot_len_transfer +=wlen;
			gp_usb_path->bot_len-=wlen;
			
			if ((gp_usb_path->bot_len ==0) )
			{
				gp_usb_path->in_tx=0;
				bulk_set_csw_status(CASEOK,gp_usb_path->bulk_hdr.csw.dCSW_DataResidue - gp_usb_path->bot_len_transfer);
				//RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0);
				gp_usb_path->bot_state = BOTFSM_CSW;
				return;
			}

#else
			if (gp_usb_path->usb_sec_off + wlen >= 512)
			{
				//len = 512 - gp_usb_path->usb_sec_off;
				ustc_memcpy_32(gp_usb_path->usb_cache+gp_usb_path->usb_sec_off,gp_usb_path->ep_buff_rx,wlen>>2);
				//store the info
//				usb_store_isector(gp_usb_path->p_para_usb,gp_usb_path->usb_cache,gp_usb_path->udisk_disk_src));	
				gp_usb_path->usb_sec_off = 0;
				
			}
			else
			{
				ustc_memcpy_32(gp_usb_path->usb_cache+gp_usb_path->usb_sec_off,gp_usb_path->ep_buff_rx,wlen>>2);
				gp_usb_path->usb_sec_off += wlen;
			}
			if (gp_usb_path->bot_len > wlen)
				gp_usb_path->bot_len -=wlen;
			else
				gp_usb_path->bot_len = 0;
			gp_usb_path->bot_len_transfer +=wlen;
			
			if ((gp_usb_path->bot_len ==0) )
			{
				bulk_set_csw_status(CASEOK,gp_usb_path->bulk_hdr.csw.dCSW_DataResidue - gp_usb_path->bot_len_transfer);
				//RBC_BuildSenseData(SCSI_SENSE_ILLEGAL_REQUEST,SCSI_ADSENSE_ILLEGAL_COMMAND,0);
				gp_usb_path->bot_state = BOTFSM_CSW;
				return;
			}
#endif
		}
	}		
}

